#!/usr/bin/env python
# -*- coding: utf-8 -*-

'''
TurboMail 3.0 integration for Webpyte / CherryPy 3.

The application configuration should contain a [mailer] section such as:

    [mailer]
    mail.brand    = False
    mail.encoding = "utf-8"
    mail.from     = "postmaster@your.domain"
    mail.smtp.server   = "smtp.your.domain"
    mail.smtp.username = "username_here"
    mail.smtp.password = "secret"
    mail.smtp.tls      = None
    mail.smtp.debug    = False

Importing this module starts TurboMail and makes it use an ImmediateManager
and the SMTP Transport.

Webpyte uses the Mailer class in this module in order to, when an application
error occurs, send an e-mail to the developer.

SendMail is an old, more or less clumsy class that used to simplify
sending e-mail in some situations. I don't like it anymore.
'''


# http://docs.python.org/whatsnew/pep-328.html
from __future__ import absolute_import
from __future__ import print_function   # deletes the print statement
from __future__ import unicode_literals # unicode by default

import logging, sys, turbomail
from logging.handlers import RotatingFileHandler
from cherrypy  import request
from . import ShowUserThisException
from .email_validator import EmailValidator


# Configure logging for turbomail
log = logging.getLogger("turbomail")
# log.addHandler(logging.StreamHandler(sys.stdout))
log.addHandler(RotatingFileHandler("logs/turbomail", maxBytes=999999,
                                   backupCount=2))
log.setLevel(logging.DEBUG) # TODO: make configurable


class PerAppConfig(object):
    '''Mapping type that delegates to request.app.config['mailer'].
    Turbomail gets some configuration at start time, so the following keys
    cannot be configured individually by app. Instead they are hardcoded:
    mail.on = True
    mail.manager = 'immediate'
    mail.transport = 'smtp'
    '''
    def __init__(self):
	self.defaults = {
	    'mail.on':True,
	    'mail.manager':'immediate',
	    'immediate': ImmediateManager,
	    'mail.transport':'smtp',
	    'smtp': SMTPTransportFactory(),
	    'mail.message.encoding':'utf8',
	}
    
    def get(self, key, default=None):
	if not request.app:
	    d = self.defaults.get(key, default)
            #print('default', key, '=', d)
	    return d
	return request.app.config.get('mailer', {}) \
	    .get(key, self.defaults.get(key, default))
    
    def update(self, adict):
	raise NotImplementedError()
        print('Should update mail config with this dict:')
        print(adict)


from turbomail.managers.immediate import ImmediateManager
from turbomail.transports.smtp import SMTPTransportFactory
from turbomail.control import interface
interface.start(PerAppConfig())
from turbomail.extensions.utf8qp import interface
interface.start()
del interface

class Mailer(object):
    exc_sent = _("A programmer has been notified and will try to fix the "
                 "problem as soon as possible.")
    
    exc_not_sent = _("""The problem was actually so bad that
we couldn't even send an e-mail to our team to sort the problem out!
If you keep getting this message, please send us an e-mail with some
information about what you were doing when this happened, so we can
try and smooth things over...""")
    
    html = _("""
<!DOCTYPE html PUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title>An error ocurred.</title>
  <style type="text/css">
    @import "/wp/tags.css";
    @import "/wp/colors.css";
  </style>
</head>

<body><div id="Dimensions" class="Pad PadMuch" style="margin:auto;">
  <h1>500 - Internal server error</h1>

  <p>Something went wrong in the server and we weren't expecting it. Sorry about that.</p>

  <p>This is commonly known as an &quot;Error 500  &quot; or
    &quot;Internal Server Error&quot;. It means we messed up.</p>
  
  <p>%s</p>
  
  <p>The error was: <strong>%s</strong></p>
  
  <p><a href="javascript:window.back();">You can press the "<strong>back</strong>" button in your browser</a> to try again, or go to the <a href="/">home page</a>.</p>
  </div></body></html>""")
    
    def handle_error(self):
        """If you don't override this method, what it does is:
        
        1) Serve the contents of a "error.html" file, and
        2) Use turbomail to send you the last exception that occurred.
        
        For this method to work, the "developer_email" instance variable must
        be set.
        """
        from cherrypy import request, response
        response.status = 500
        import traceback
        #e = traceback.format_exc() #repr(sys.exc_info())
        # print(repr(sys.exc_info()))
        typ, value, trace = sys.exc_info()
        host = request.headers["host"]
        try:
            plain = '\n'.join([
                host + request.path_info,
                unicode(typ),
                unicode(value),
                '',
                ''.join(traceback.format_tb(trace)) or "(nothing)",
                '',
                'STATE:',
                repr(request.exception_state) if hasattr(request, 'exception_state') else '',
            ])
            self.send_to_developer(subject="Error at " + host, plain=plain)
        except:
            was_msg_sent = self.exc_not_sent
        else:
            was_msg_sent = self.exc_sent
        try:
            f = open("error.html", "r")
            html = f.read()
            f.close()
        except:
            html = self.html % (was_msg_sent, value)
        response.body = [html]
    
    def send_to_developer(self, subject, plain):
        dest = self.developer_email
        msg = turbomail.Message(dest, dest, subject, plain=plain)
        msg.send()



class SendMail(object):
    """Instances of this class can easily send out e-mail messages
    by using TurboMail. It was originally written for TurboMail 2, and it is
    working fine with TurboMail 3, but I don't know anymore how much of its
    code is still necessary. TODO: Study TurboMail 3
    """
    email_validator = EmailValidator()
    
    def validate_email(self, address):
        if isinstance(address, basestring):
            name = None
            email = address
        else:
            name = address[0]
            email = address[1]
        email, err = self.email_validator.validate(email)
        if err:
            raise ShowUserThisException(err)
        else:
            return name, email
    
    def recipients():
        doc = """Gets or sets the destination e-mail(s).
        
        E-mail addresses can be represented as any of the following:
        - A string
        - A 2-tuple of ("Full Name", "name@host.tld")
        - A list of strings or 2-tuples
        """
        def fget(self):  return self._recipients #emails.split(",")
        def fset(self, val):
            if isinstance(val, basestring):
                self._recipients = [self.validate_email(val)]
            else:
                self._recipients = map(self.validate_email, val)
        return locals()
    recipients = property(**recipients())
    
    def replyto():
        doc = """Gets or sets the replyto e-mail(s).
        
        E-mail addresses can be represented as any of the following:
        - A string
        - A 2-tuple of ("Full Name", "name@host.tld")
        - A list of strings or 2-tuples
        """
        def fget(self):  return self._replyto
        def fset(self, val):
            if isinstance(val, basestring):
                self._replyto = [self.validate_email(val)]
            else:
                self._replyto = map(self.validate_email, val)
        return locals()
    replyto = property(**replyto())
    
    def set_subject_prefixed_by_domain(self, subject):
        domain = request.headers['Host'].replace("www.", "")
        self.subject = "[%s] %s" % (domain, subject)
    
    def _getmessage(self):
        """Returns the assembled message for TurboMail to send."""
        config = request.app.config.get("mailer", {})
        msg = turbomail.Message(author=config.get("mail.from"),
                                to=self.recipients,
                                subject=self.subject)
        if self.replyto:  msg.reply_to = self.replyto
        msg.plain = self.body
        msg.rich  = self.html
        return msg
    
    def send(self):
        m = self._getmessage()
        m.send()
        return "Mensagem enviada."
    
    def __init__(self, body, recipients, replyto=[], subject="", html=None):
        self.body = body
        self.recipients = recipients
        self.replyto = replyto
        self.subject = subject
        self.html = html
